from contextlib import contextmanager
from typing import Any, Optional, List, Dict
import redis
import json
import pickle

from comtypes import logger

import config
from src.utils.logger import get_logger
logger = get_logger("capcut")


class RedisHelper:
    """Redis工具类"""
    # 全局实例
    _global_instance: 'RedisHelper' = None

    @classmethod
    def initialize(cls) -> 'RedisHelper':
        """初始化全局Redis实例"""
        if cls._global_instance is None:
            cls._global_instance = cls()
        return cls._global_instance

    @classmethod
    def get_instance(cls) -> 'RedisHelper':
        """获取全局Redis实例"""
        if cls._global_instance is None:
            raise RuntimeError("Redis实例未初始化，请先调用initialize()方法")
        return cls._global_instance

    def __init__(self):
        """构造函数"""
        self._redis: redis.Redis = None
        self._pool: redis.ConnectionPool = None
        self._init_redis()

    def _init_redis(self):
        """初始化Redis连接池"""
        try:
            self._pool = redis.ConnectionPool(
                **config.REDIS_CONFIG,
                max_connections=10,  # 最大连接数
                socket_timeout=5,  # socket超时时间
                socket_connect_timeout=5,  # 连接超时时间
                retry_on_timeout=True  # 超时时重试
            )
            self._redis = redis.Redis(connection_pool=self._pool)
            self._redis.ping()  # 测试连接
            logger.info("Redis连接池初始化成功")
        except Exception as e:
            logger.error(f"Redis连接池初始化失败: {str(e)}")
            raise

    def _serialize_value(self, value: Any) -> bytes:
        """序列化值"""
        if isinstance(value, (str, int, float, bool)):
            return str(value).encode('utf-8')
        elif isinstance(value, bytes):
            return value
        else:
            # 对复杂对象使用pickle序列化
            return pickle.dumps(value)

    def _deserialize_value(self, value: bytes, return_type: str = 'auto') -> Any:
        """反序列化值"""
        if value is None:
            return None

        try:
            if return_type == 'str':
                return value.decode('utf-8')
            elif return_type == 'int':
                return int(value.decode('utf-8'))
            elif return_type == 'float':
                return float(value.decode('utf-8'))
            elif return_type == 'bool':
                return value.decode('utf-8').lower() in ('true', '1', 'yes')
            elif return_type == 'bytes':
                return value
            elif return_type == 'json':
                return json.loads(value.decode('utf-8'))
            else:
                # 自动判断类型
                try:
                    # 先尝试pickle反序列化
                    return pickle.loads(value)
                except:
                    # 如果失败，返回字符串
                    return value.decode('utf-8')
        except Exception as e:
            logger.warning(f"反序列化失败: {str(e)}, 返回原始bytes")
            return value

    @classmethod
    @contextmanager
    def pipeline(cls, transaction: bool = True):
        """
        管道上下文管理器

        Args:
            transaction: 是否使用事务

        Yields:
            redis.Redis.pipeline: Redis管道对象
        """
        instance = cls.get_instance()
        pipeline = instance._redis.pipeline(transaction=transaction)

        try:
            yield pipeline
            pipeline.execute()
        except Exception as e:
            logger.error(f"管道执行失败: {str(e)}")
            pipeline.reset()
            raise

    @classmethod
    def execute_pipeline(cls, commands: List[Dict[str, Any]]) -> List[Any]:
        """
        执行一系列Redis命令

        Args:
            commands: 命令列表，每个命令是一个字典，包含:
                     - method: 方法名 (如 'set', 'get', 'hset', 'hgetall'等)
                     - args: 位置参数列表
                     - kwargs: 关键字参数字典

        Returns:
            命令执行结果列表
        """
        try:
            instance = cls.get_instance()
            pipeline = instance._redis.pipeline()

            for cmd in commands:
                method = getattr(pipeline, cmd['method'])
                args = cmd.get('args', [])
                kwargs = cmd.get('kwargs', {})
                method(*args, **kwargs)

            return pipeline.execute()
        except Exception as e:
            logger.error(f"执行管道命令失败: {str(e)}")
            return []

    @classmethod
    def get(cls, key: str, return_type: str = 'auto') -> Any:
        """
        获取值（使用全局实例）

        Args:
            key: Redis键
            return_type: 返回类型 ('auto', 'str', 'int', 'float', 'bool', 'bytes', 'json')

        Returns:
            值或None
        """
        try:
            instance = cls._global_instance
            if instance is None:
                logger.error("Redis实例未初始化")
                return None

            value = instance._redis.get(key)
            return instance._deserialize_value(value, return_type)
        except Exception as e:
            logger.error(f"Redis get操作失败 key={key}: {str(e)}")
            return None

    @classmethod
    def set(
            cls,
            key: str,
            value: Any,
            ex: Optional[int] = None,
            px: Optional[int] = None,
            nx: bool = False,
            xx: bool = False
    ) -> bool:
        """
        设置值（使用全局实例）

        Args:
            key: Redis键
            value: 要存储的值
            ex: 过期时间(秒)
            px: 过期时间(毫秒)
            nx: 只在键不存在时设置
            xx: 只在键存在时设置

        Returns:
            True if successful, False otherwise
        """
        try:
            instance = cls._global_instance
            if instance is None:
                logger.error("Redis实例未初始化")
                return False

            serialized_value = instance._serialize_value(value)
            result = instance._redis.set(key, serialized_value, ex=ex, px=px, nx=nx, xx=xx)
            return bool(result)
        except Exception as e:
            logger.error(f"Redis set操作失败 key={key}: {str(e)}")
            return False

    @classmethod
    def get_json(cls, key: str) -> Optional[dict]:
        """获取JSON格式的值"""
        return cls.get(key, return_type='json')

    @classmethod
    def set_json(cls, key: str, value: dict, ex: Optional[int] = None) -> bool:
        """设置JSON格式的值"""
        try:
            json_str = json.dumps(value, ensure_ascii=False)
            return cls.set(key, json_str, ex=ex)
        except Exception as e:
            logger.error(f"Redis set_json操作失败 key={key}: {str(e)}")
            return False

    @classmethod
    def exists(cls, key: str) -> bool:
        """检查键是否存在"""
        try:
            instance = cls._global_instance
            if instance is None:
                logger.error("Redis实例未初始化")
                return False

            return bool(instance._redis.exists(key))
        except Exception as e:
            logger.error(f"Redis exists操作失败 key={key}: {str(e)}")
            return False

    @classmethod
    def delete(cls, *keys: str) -> int:
        """删除键"""
        try:
            instance = cls._global_instance
            if instance is None:
                logger.error("Redis实例未初始化")
                return 0

            return instance._redis.delete(*keys)
        except Exception as e:
            logger.error(f"Redis delete操作失败 keys={keys}: {str(e)}")
            return 0

    @classmethod
    def expire(cls, key: str, seconds: int) -> bool:
        """设置键的过期时间"""
        try:
            instance = cls._global_instance
            if instance is None:
                logger.error("Redis实例未初始化")
                return False

            return bool(instance._redis.expire(key, seconds))
        except Exception as e:
            logger.error(f"Redis expire操作失败 key={key}: {str(e)}")
            return False

    @classmethod
    def ttl(cls, key: str) -> int:
        """获取键的剩余生存时间"""
        try:
            instance = cls._global_instance
            if instance is None:
                logger.error("Redis实例未初始化")
                return -2

            return instance._redis.ttl(key)
        except Exception as e:
            logger.error(f"Redis ttl操作失败 key={key}: {str(e)}")
            return -2

    @classmethod
    def zadd(cls, key: str, mapping: dict) -> int:
        """添加成员到Sorted Set"""
        try:
            instance = cls.get_instance()
            if instance is None:
                logger.error("Redis实例未初始化")
                return 0
            return instance._redis.zadd(key, mapping)
        except Exception as e:
            logger.error(f"Redis zadd操作失败 key={key}: {str(e)}")
            return 0

    @classmethod
    def zrange(cls, key: str, start: int, end: int) -> list:
        """获取Sorted Set中的成员范围"""
        try:
            instance = cls.get_instance()
            if instance is None:
                logger.error("Redis实例未初始化")
                return []
            return instance._redis.zrange(key, start, end)
        except Exception as e:
            logger.error(f"Redis zrange操作失败 key={key}: {str(e)}")
            return []

    @classmethod
    def zcard(cls, key: str) -> int:
        """获取Sorted Set中的成员数量"""
        try:
            instance = cls.get_instance()
            if instance is None:
                logger.error("Redis实例未初始化")
                return 0
            return instance._redis.zcard(key)
        except Exception as e:
            logger.error(f"Redis zcard操作失败 key={key}: {str(e)}")
            return 0

    @classmethod
    def zrem(cls, key: str, *members) -> int:
        """从Sorted Set中删除成员"""
        try:
            instance = cls.get_instance()
            if instance is None:
                logger.error("Redis实例未初始化")
                return 0
            return instance._redis.zrem(key, *members)
        except Exception as e:
            logger.error(f"Redis zrem操作失败 key={key}: {str(e)}")
            return 0

    @classmethod
    def hset(cls, key: str, mapping: dict) -> bool:
        """设置哈希字段"""
        try:
            instance = cls.get_instance()
            if instance is None:
                logger.error("Redis实例未初始化")
                return False
            result = instance._redis.hset(key, mapping=mapping)
            return result > 0
        except Exception as e:
            logger.error(f"Redis hset操作失败 key={key}: {str(e)}")
            return False

    @classmethod
    def hgetall(cls, key: str) -> dict:
        """获取哈希所有字段"""
        try:
            instance = cls.get_instance()
            if instance is None:
                logger.error("Redis实例未初始化")
                return {}
            result = instance._redis.hgetall(key)
            return {k.decode(): v.decode() for k, v in result.items()}
        except Exception as e:
            logger.error(f"Redis hgetall操作失败 key={key}: {str(e)}")
            return {}

    @classmethod
    def sadd(cls, key: str, *members) -> int:
        """添加集合成员"""
        try:
            instance = cls.get_instance()
            if instance is None:
                logger.error("Redis实例未初始化")
                return 0
            return instance._redis.sadd(key, *members)
        except Exception as e:
            logger.error(f"Redis sadd操作失败 key={key}: {str(e)}")
            return 0

    @classmethod
    def smembers(cls, key: str) -> list:
        """获取集合所有成员"""
        try:
            instance = cls.get_instance()
            if instance is None:
                logger.error("Redis实例未初始化")
                return []
            members = instance._redis.smembers(key)
            return [member.decode() for member in members]
        except Exception as e:
            logger.error(f"Redis smembers操作失败 key={key}: {str(e)}")
            return []

    def close(self):
        """关闭连接池"""
        if self._pool:
            self._pool.disconnect()
            logger.info("Redis连接池已关闭")

    @classmethod
    def close_global(cls):
        """关闭全局实例的连接池"""
        if cls._global_instance:
            cls._global_instance.close()
            cls._global_instance = None

   # 基本操作示例
    # 设置字符串
    # RedisHelper.set("user:1:name", "张三", ex=3600)  # 1小时过期
    # name = RedisHelper.get("user:1:name", return_type='str')
    # print(f"用户名: {name}")

